package com.sticktogether.navigation;
import java.util.ArrayList;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

import com.sticktogether.R;
import com.sticktogether.server.ServerCommunication;

/**
 * A Step contains all information about one step of the route the user needs to go.
 * 
 * Note:
 * Some code parts are commented because they are not needed and thus save computing time. 
 * They are not deleted, in case the information will be needed later.
 * 
 * @author 	David Schattel
 * @since 	01.12.2012
 */
public class Step {

	/**
	 * Parameters containing response information
	 */
	private Document directionsResponseDocument;
	private NodeList resultNodeList = null;
	private ArrayList<Step> subSteps = new ArrayList<Step>();
	
	/**
	 * For navigation in response
	 */
	private String stepPrefix; 
	private int parentId;	
	private int depth;			//depth in "nested steps" (the deeper, the more details about the travelling)
	private XPath xpath;
	//private int id;
	
	/**
	 * Results after analyzing the response
	 */
	private String travel_mode;
	private Walk walk;
	private Transit transit;
	//private String points;
	
	/**
	 * One Step is defined with an unique id in each depth of detail
	 * 
	 * @param directionsResponseDocument	xml document with response
	 * @param xpath
	 * @param stepPrefix					prefix for navigation in document
	 * @param id							step id in the current depth
	 * @param parentId						id of the parent step
	 * @param depth							depth of information detail
	 */
	public Step(Document directionsResponseDocument, XPath xpath, String stepPrefix, int id, int parentId, int depth){
//		this.id = id;
		this.directionsResponseDocument = directionsResponseDocument;
		this.xpath = xpath;
		this.parentId = parentId;
		this.depth = depth;
		this.stepPrefix = stepPrefix + id + "]/";
		
		readDocument();
		checkOut_subSteps();
	}
	
	/**
	 * Read the response
	 * 
	 * 
	 */
	public void readDocument() {
	    this.travel_mode = getString(stepPrefix + "travel_mode");    
	    
	    // if one needs to walk
	    if(travel_mode.equals("WALKING")){
	    	this.walk = getWalkInfo();
	    }
	    
	    // if one needs to take public transport
	    if(travel_mode.equals("TRANSIT")){
	    	this.transit = getTransitInfo();
	    }	    
	}
	
	/**
	 * Check if there is more detailed information about the current step
	 * 
	 */
	private void checkOut_subSteps() {
    	String prefix = "";
	    int step = 1;
    	
	    // loop over all steps
    	while(true){
    		
    		// get travel mode and break if there is no more travel mode/no steps
    		prefix = this.stepPrefix + "/step[";
    		resultNodeList = getNodeList(prefix + step + "]/" + "travel_mode");
    		
	    	if(resultNodeList.getLength() == 0)
	    		break;	    	
		    // a new step with increased depth
	    	this.subSteps.add(new Step(this.directionsResponseDocument, this.xpath, prefix, step, this.parentId, this.depth+1));
		    if(this.subSteps.get(this.subSteps.size()-1).getWalkInfo().getInstruction().equals(ServerCommunication.map.getString(R.string.no_instructions)))
		    	this.subSteps.remove(this.subSteps.size()-1);
	    	
		    step++;
    	}
    	
	}
	

	
	/**
	 * Provide general information (standard for a walking route)
	 * @return	Walk instance
	 * 
	 */
	private Walk getWalkInfo() {
		//final Location start = getLocation(stepPrefix + "start_location");	  
		//final Location end = getLocation(stepPrefix + "end_location");
		  
		//final int duration = Integer.parseInt(getString(stepPrefix + "duration/value"));
		    
		final String durationText = getString(stepPrefix + "duration/text");
		    
		//final int distance = Integer.parseInt(getString(stepPrefix + "distance/value"));
		    
		final String distanceText = getString(stepPrefix + "distance/text");
		  
		resultNodeList = getNodeList(stepPrefix + "html_instructions");
		String instruction = ""; 
		for(int i=0; i<resultNodeList.getLength(); ++i) {
			instruction += resultNodeList.item(i).getTextContent();
			if(i != resultNodeList.getLength() -1)
				instruction += "\n";
		}
		if(instruction.equals(""))
			instruction = ServerCommunication.map.getString(R.string.no_instructions);
		
		//this.points = getString(stepPrefix + "polyline/points");
		  
		//return new Walk(start, end, duration, durationText, distance, distanceText, android.text.Html.fromHtml(instruction).toString());
		return new Walk(durationText, distanceText, android.text.Html.fromHtml(instruction).toString());
		
	}
	
	/**
	 * Provide transit details
	 * @return	Transit instance
	 * 
	 */
	private Transit getTransitInfo() {
		final String prefix = stepPrefix + "transit_details/";
	  	  final Walk walkInfo = getWalkInfo();
	  	  
	  	  
	  	  final String depName = getString(prefix + "departure_stop/name");
	  	  //final Location depLocation = getLocation(prefix + "departure_stop/location");
	  	  
	  	  final String arrName = getString(prefix + "arrival_stop/name");
	  	  //final Location arrLocation = getLocation(prefix + "arrival_stop/location");
	  	  
	  	  final Line line = getLineInfo(prefix);
	  	  
	  	  final int num_stops = Integer.parseInt(getString(prefix + "num_stops"));
	  	  
	  	  //final String headsign = getString(prefix + "headsign");
	  	  
	  	  //final long depTime = Long.parseLong(getString(prefix + "departure_time/value"));
	  	  final String depTimeText = getString(prefix + "departure_time/text");
	  	  //final String depTimeZone = getString(prefix + "departure_time/time_zone");
	  	  
	  	  //final long arrTime = Long.parseLong(getString(prefix + "arrival_time/value"));
	  	  final String arrTimeText = getString(prefix + "arrival_time/text");
	  	  //final String arrTimeZone = getString(prefix + "arrival_time/time_zone");
	  	  
	  	  //return new Transit(walkInfo, depName, depLocation, arrName, arrLocation, line, num_stops, headsign, depTime, depTimeText, depTimeZone, arrTime, arrTimeText, arrTimeZone);
	  	return new Transit(walkInfo, depName, arrName, line, num_stops, depTimeText,arrTimeText);
	}

	/**
	 * Provide detailed information about public transport line
	 * @return	Line instance
	 * 
	 */
	private Line getLineInfo(String prefix) {
		  prefix = prefix + "line/";
		  
		  //final String name_line = getString(prefix + "name"); 
		  
		  final String type_line = getString(prefix + "short_name");
		  
		  //final String color = getString(prefix + "color");
		  
		  //final String textcolor = getString(prefix + "text_color");
		  
		  // vehicle information
		  String prefix2 = prefix + "vehicle/";
		  
		  //final String name_vehicle = getString(prefix2 + "name");
		  
		  final String type_vehicle = getString(prefix2 + "type");
		  
		  final String iconURL = getString(prefix2 + "icon");
		  
		  final String local_iconURL = getString(prefix2 + "local_icon");
		  
		  // agency information
		  prefix2 = prefix + "agency/";
		  
		  final String agency_name = getString(prefix2 + "name");
		  
		  //final String agency_URL = getString(prefix2 + "url");
		  
		  //final String agency_phone = getString(prefix2 + "phone");
		  
		  //return new Line(name_line, type_line, name_vehicle, type_vehicle, iconURL, local_iconURL, color, textcolor, agency_name, agency_URL, agency_phone);
		  return new Line(type_line, type_vehicle, iconURL, local_iconURL, agency_name);
		  //public Line(String type_line, String type_vehicle, String iconURL, String agency_name){
	  }
	
	/**
	 * Read location and return it
	 * @param pathExpression Path to location information
	 * @return	Location with latitude and longitude
	 * 
	 */
/*	private Location getLocation(String pathExpression) {
		Location location = new Location("");
		location.setLatitude(Float.NaN);
		location.setLongitude(Float.NaN);
		resultNodeList = getNodeList(pathExpression + "/*");	  
		  for(int i=0; i<resultNodeList.getLength(); ++i) {
		    Node node = resultNodeList.item(i);
		    if("lat".equals(node.getNodeName())) location.setLatitude(Float.parseFloat(node.getTextContent()));
		    if("lng".equals(node.getNodeName())) location.setLongitude(Float.parseFloat(node.getTextContent()));
		  }
		return location;
	}*/
	
	/**
	 * Get String expression of nodelist if it's not null
	 * @param expression	XPath expression
	 * @return	String response
	 */
	private String getString(String expression) {
		resultNodeList = getNodeList(expression);
		if(resultNodeList.getLength() != 0)
			return resultNodeList.item(0).getTextContent();
		return null;
	}
	
	/**
	 * Evaluate a given path expression for the object's xpath (reducing code amount)
	 * @param expression Path expression
	 * @return NodeList instance containing desired content
	 * 
	 */
	private NodeList getNodeList(String expression) {
		  try {
			return (NodeList) xpath.evaluate(expression, directionsResponseDocument, XPathConstants.NODESET);
		} catch (XPathExpressionException e) {
			e.printStackTrace();
			return null;
		}
	  }
	
	/**
	 * Get the way of traveling
	 * @return TravelMode
	 */
	public String getTravelMode(){
		return this.travel_mode;
	}
	
	/**
	 * Get the general details of the step
	 * @return Walk instance
	 */
	public Walk getGeneralDetails(){
		return this.walk;
	}
	
	/**
	 * Get the transit details of the step
	 * @return Transit instance
	 */
	public Transit getTransitDetails(){
		if(this.travel_mode.equals("WALKING"))
			return null;
		return this.transit;
	}
	
	/**
	 * Get the number of sub steps
	 * @return step number
	 */
	public int getNumSubStep(){
		return this.subSteps.size();
	}
	
	/**
	 * Get the more detailed steps
	 * @return ArrayList of Step instances
	 */
	public ArrayList<Step> getSubSteps(){
		return this.subSteps;
	}
	
	
	/**
	 * Get the step's ID
	 * @return ID
	 *//*
	public int getId(){
		return this.id;
	}
	
	*/
	/**
	 * Get Polyline information
	 * @return Polyline
	 *//*
	public String getPoints(){
		return this.points;
	}
	*/
}
